#include <jni.h>
#include <string>
#include "log.h"
#include "android/bitmap.h"
#include "png.h"

#define PNG_BYTES_TO_CHECK 4
char buf[PNG_BYTES_TO_CHECK];
#define argb(a, r, g, b) ( ((a) & 0xff) << 24 ) | ( ((b) &0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myImageView_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

typedef struct PNG {
    png_structp png_ptr;
    png_infop info_ptr;
} PNG;

extern "C"
JNIEXPORT jint JNICALL
Java_com_youlu_myapplication_PngHandler_nativeGetWidth(JNIEnv *env, jobject thiz, jlong address) {

    PNG *png_p = reinterpret_cast<PNG *>(address);
    png_structp png_ptr = png_p->png_ptr;
    png_infop info_ptr = png_p->info_ptr;
    int width = png_get_image_width(png_ptr, info_ptr);
    return width;
}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_youlu_myapplication_PngHandler_nativeInit(JNIEnv *env, jobject thiz, jstring path) {

    const char *file = env->GetStringUTFChars(path, nullptr);

    FILE *fp = fopen(file, "rb");
    if (fp == nullptr) {
        LOGI("load_png_image err:fp == NULL");
        return 0;
    }

    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
    png_infop info_ptr = png_create_info_struct(png_ptr);
    setjmp(png_jmpbuf(png_ptr));
    int tmp = fread(buf, 1, PNG_BYTES_TO_CHECK, fp);
    if (tmp < PNG_BYTES_TO_CHECK) {
        fclose(fp);
        png_destroy_read_struct(&png_ptr, &info_ptr, 0);
        LOGI("load_png_image err: png check fail");
        return 0;
    }

    tmp = png_sig_cmp((png_bytep) buf, 0, PNG_BYTES_TO_CHECK);
    if (tmp != 0) {
        fclose(fp);
        png_destroy_read_struct(&png_ptr, &info_ptr, 0);
        LOGI("load_png_image err: not png format");
        return 0;
    }

    rewind(fp);
    png_init_io(png_ptr, fp);
    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);

    PNG *p = (PNG *) malloc(sizeof(PNG));
    memset(p, 0, sizeof(PNG));
    p->png_ptr = png_ptr;
    p->info_ptr = info_ptr;

    fclose(fp);

    return (jlong) p;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_youlu_myapplication_PngHandler_nativeGetHeight(JNIEnv *env, jobject thiz, jlong address) {
    PNG *png_p = reinterpret_cast<PNG *>(address);
    png_structp png_ptr = png_p->png_ptr;
    png_infop info_ptr = png_p->info_ptr;
    int height = png_get_image_height(png_ptr, info_ptr);
    return height;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_youlu_myapplication_PngHandler_nativeUpdateBitmap(JNIEnv *env, jobject thiz,
                                                           jobject bitmap, jlong address) {

    PNG *png_p = reinterpret_cast<PNG *>(address);
    png_structp png_ptr = png_p->png_ptr;
    png_infop info_ptr = png_p->info_ptr;

    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    int height = info.height;
    int width = info.width;
    int color_type = png_get_color_type(png_ptr, info_ptr);

    void *address_ptr;
    AndroidBitmap_lockPixels(env, bitmap, &address_ptr);
    png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
    unsigned int row_bytes = png_get_rowbytes(png_ptr, info_ptr);

    auto buffer = (png_bytep) address_ptr;

    LOGI("color_type is %d", color_type);
    LOGI("info->width is %d", info.width);
    LOGI("info->stride is %d", info.stride);
    for (int i = 0; i < height; ++i) {
        memcpy(buffer + (row_bytes * i), row_pointers[i], row_bytes);
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_youlu_myapplication_PngHandler_nativeTestBitmap(JNIEnv *env, jobject thiz,
                                                         jobject bitmap) {
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    int w = info.width;
    int h = info.height;

    void *address_ptr;

    AndroidBitmap_lockPixels(env, bitmap, &address_ptr);

    auto *pixel = (int *) address_ptr;
    int left = 0;
    for (int i = 0; i < h; i++) {
        left = i * w;
        for (int j = left; j < left + w; j++) {

            pixel[j] = argb(0XFF, 0, 0, 0);
//            LOGI("%d::  %x\n",j,pixel[j]);
        }
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_youlu_myapplication_MainActivity_nativeTestBitmap(JNIEnv *env, jobject thiz,
                                                           jobject bitmap) {
    AndroidBitmapInfo info;
    AndroidBitmap_getInfo(env, bitmap, &info);
    int w = info.width;
    int h = info.height;

    void *address_ptr;

    AndroidBitmap_lockPixels(env, bitmap, &address_ptr);

    auto *pixel = (int *) address_ptr;
    int left = 0;
    for (int i = 0; i < h; i++) {
        left = i * w;
        for (int j = left; j < left + w; j++) {

            pixel[j] = argb(0XFF, 0, 0, 0);
//            LOGI("%d::  %x\n",j,pixel[j]);
        }
    }

    AndroidBitmap_unlockPixels(env, bitmap);
}